package edu.cmu.mism.dgjava.data.ui;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import org.eclipse.swt.SWT;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.forms.widgets.ScrolledPageBook;

import edu.cmu.mism.dgjava.data.ui.composites.AbstractDataComposite;
import edu.cmu.mism.dgjava.utils.SWTUtils;

/**
 * 
 * The Parameters Type book is designed to hold data composite for different
 * data type. A type book instance contains one drop down box control for
 * different composite types and displays one data composite at one time for the
 * selected type.
 * 
 * @author Christian
 * 
 */
public class CommonParametersTypeBook {

	private ScrolledPageBook _book;
	private HashMap<String, AbstractDataComposite> _typeDataCompMap;
	private Combo _typeComb;
	private Combo _parentComb;
	private Map<String, Collection<String>> pc;

	/**
	 * @param parent
	 * @param typeLabel
	 */
	public CommonParametersTypeBook(Composite parent, String typeLabel,
			Properties typeDataCompProperties) {

		// initiates the data composite map
		initTypeDataCompMap(typeDataCompProperties);

		// creates the type combo box control
		createTypeCombo(parent, typeLabel);

		// creates pages for all data composites
		createPageControl(parent);

		selectParentCategory();

		// by default selects the first item within the type box
		this._typeComb.select(0);

		showPageAtSelection();
	}

	/**
	 * @param typeDataCompProperties
	 */
	private void initTypeDataCompMap(Properties typeDataCompProperties) {

		this._typeDataCompMap = new HashMap<String, AbstractDataComposite>();
		Set<Object> keys = typeDataCompProperties.keySet();
		for (Object key : keys) {
			if (key instanceof String) {
				String type = (String) key;
				String className = typeDataCompProperties.getProperty(type);
				AbstractDataComposite dataComposite = null;

				try {
					dataComposite = (AbstractDataComposite) Class.forName(
							className).newInstance();
				} catch (InstantiationException e) {
					// TODO add logger support
				} catch (IllegalAccessException e) {
					// TODO add logger support
				} catch (ClassNotFoundException e) {
					// TODO add logger support
				}

				if (dataComposite != null) {
					this._typeDataCompMap.put(type, dataComposite);
				}

			}
		}

	}

	/**
	 * @param parent
	 * @param typeLabel
	 */
	private void createTypeCombo(Composite parent, String typeLabel) {

		Map<String, Collection<String>> catMap = getCategory();
		if (catMap == null) {
			Composite typeContainer = SWTUtils.createComposite(parent, 2, -1,
					1, GridData.FILL_HORIZONTAL);

			GridData hGd = new GridData(GridData.FILL_HORIZONTAL);

			SWTUtils.createLabel(typeContainer, typeLabel, 1);
			this._typeComb = SWTUtils.createCombo(typeContainer,
					this._typeDataCompMap.keySet().toArray(new String[0]), 1);

			// this._typeComb.select(0);
			this._typeComb.setLayoutData(hGd);

			this._typeComb.addSelectionListener(new SelectionListener() {

				@Override
				public void widgetSelected(SelectionEvent e) {
					showPageAtSelection();
				}

				@Override
				public void widgetDefaultSelected(SelectionEvent e) {
				}
			});
		} else {
			Composite typeContainer = SWTUtils.createComposite(parent, 3, -1,
					1, GridData.FILL_HORIZONTAL);

			GridData hGd = new GridData(GridData.FILL_HORIZONTAL);

			SWTUtils.createLabel(typeContainer, typeLabel, 1);
			this._parentComb = SWTUtils.createCombo(typeContainer, catMap
					.keySet().toArray(new String[0]), 1);
			this._parentComb.addSelectionListener(new SelectionListener() {

				@Override
				public void widgetSelected(SelectionEvent e) {
					selectParentCategory();
				}

				@Override
				public void widgetDefaultSelected(SelectionEvent e) {

				}
			});
			this._parentComb.select(0);

//			SWTUtils.createLabel(typeContainer, "Pricing Model:", 1);
			this._typeComb = SWTUtils.createCombo(typeContainer, new String[0],
					1);
			this._typeComb.setLayoutData(hGd);
			this._typeComb.addSelectionListener(new SelectionListener() {

				@Override
				public void widgetSelected(SelectionEvent e) {
					showPageAtSelection();
				}

				@Override
				public void widgetDefaultSelected(SelectionEvent e) {
				}
			});
		}

	}

	private void selectParentCategory() {
		if (this._parentComb != null) {
			String sel = this._parentComb.getText();
			this._typeComb.setItems(pc.get(sel).toArray(new String[0]));
			this._typeComb.select(0);
			showPageAtSelection();
		}
	}

	/**
	 * Creates control for this type book
	 */
	private void createPageControl(Composite container) {
		this._book = new ScrolledPageBook(container);
		GridData gd = new GridData(SWT.FILL, SWT.FILL, true, true);
		this._book.setLayoutData(gd);

		// iterates the type data composite map
		for (String key : this._typeDataCompMap.keySet()) {
			Composite pageComposite = _book.createPage(key);
			GridLayout gl = new GridLayout();
			gl.marginWidth = 0;
			gl.marginHeight = 0;
			pageComposite.setLayout(gl);
			AbstractDataComposite dataComposite = this._typeDataCompMap
					.get(key);
			// create controls for the given composite
			dataComposite.createControls(pageComposite);
		}

	}

	/**
	 * Shows page at current selection
	 */
	private void showPageAtSelection() {

		_book.showPage(getOptionPricingType());
	}

	/**
	 * @return the data model associated with the selected composite, the model
	 *         will be popped up with values in it
	 */
	public Object getDataObject() {

		AbstractDataComposite dataComp = this._typeDataCompMap
				.get(getOptionPricingType());
		if (dataComp != null) {
			return dataComp.getDataObject();
		}

		return null;
	}

	private Map<String, Collection<String>> getCategory() {
		if (pc == null && _typeDataCompMap != null
				&& _typeDataCompMap.size() > 0) {
			pc = new HashMap<String, Collection<String>>();
			for (String type : _typeDataCompMap.keySet()) {
				int s;
				int e;
				// check if the type combo is sub divided
				if (((s = type.lastIndexOf("[")) != -1)
						&& ((e = type.lastIndexOf("]")) != -1)) {
					String cat = type.substring(s + 1, e);
					String subCat = type.substring(e + 1, type.length());
					Collection<String> subCats = pc.get(cat);
					if (subCats == null) {
						subCats = new ArrayList<String>();
						pc.put(cat, subCats);
					}

					subCats.add(subCat);

				} else {
					// terminate the loop if no sub category structure supported
					return null;
				}

			}
		}
		return pc;
	}

	private String getOptionPricingType() {
		// get the current select type
		String selType = _typeComb.getText();
		// tweek for double selection
		if (pc != null && pc.size() > 0) {
			selType = "[" + this._parentComb.getText() + "]"
					+ this._typeComb.getText();
		}

		return selType;
	}

}
